package com.deuwise.system.controller;

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.metadata.TableStyle;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.plugins.Page;
import com.deuwise.common.annotation.DateTime;
import com.deuwise.common.exception.RException;
import com.deuwise.common.utils.*;
import com.deuwise.common.utils.cache.Cache;
import com.deuwise.config.oauth2.JWTUtil;
import com.deuwise.system.entity.AppDo;
import com.deuwise.system.entity.BackUpDo;
import com.deuwise.system.entity.LogDO;
import com.deuwise.system.entity.export.LogDoExport;
import com.deuwise.system.service.AppDoService;
import com.deuwise.system.service.BackUpDoService;
import com.deuwise.system.service.LogDoService;
import com.github.kevinsawicki.http.HttpRequest;
import io.swagger.annotations.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.server.PathParam;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author deuwise
 * @date 2018-11-09 17:12:43
 */
@Api(tags = "日志模块模块", description = "")
@RequestMapping("api/log")
@RestController()
@Validated
public class LogDoController extends AbstractController {
    @Autowired
    private LogDoService logDoService;

    @ApiOperation(value = "日志list", notes = "日志list")
    @ResponseBody
    @GetMapping("/list")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "操作名称", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "userName", value = "用户名称", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "startLogTime", value = "开始时间", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "endLogTime", value = "结束时间", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "status", value = "状态 0错误，1正常，2登录", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "export",required =false, value = "是否导出操作0导出结果条件，1导出1万条", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "currentPage", value = "开始页数", defaultValue = "1", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "pageSize", value = "每页多少条", defaultValue = "15", dataType = "int", paramType = "query")})
    public Result<Page<LogDO>> select(
            HttpServletRequest request, HttpServletResponse response,
            @DateTime(message = "您输入的格式错误，正确的格式为：{format}") @PathParam("startLogTime") String startLogTime,
            @DateTime(message = "您输入的格式错误，正确的格式为：{format}") @PathParam("endLogTime") String endLogTime,
            @RequestParam(value = "name", required = false) String name,
            @RequestParam(value = "userName",required = false)String userName,
            String export,
            @RequestParam(value = "status", required = false) String status
    ) {

        Subject subject = SecurityUtils.getSubject();

       if(StringUtils.isBlank(export)){
           if(!subject.isPermitted("sys:log:list")) {
               throw new RException("已超出你的权限范围");
           }
       }else {
           if (!subject.isPermitted("sys:log:export")) {
               throw new RException("已超出你的权限范围");
           }

       }
        Wrapper<LogDO> wrapper = new EntityWrapper<>();
        if (name != null && StringUtils.isNotBlank(name)) {
            wrapper.like("operation", name);

        }
        if (status != null && StringUtils.isNotBlank(status) && !"-1".equals(status)) {
            wrapper.eq("status", Integer.parseInt(status));
        }
        if (StringUtils.isNotBlank(userName)) {
            wrapper.like("user_name", userName);
        }
        Date startDate = null;
        Date endDate = null;
        DateFormat fmt = new SimpleDateFormat("yyyy-MM-dd");
        if (!StringUtils.isEmpty(startLogTime)) {
            try {
                startDate = fmt.parse(startLogTime);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        if (!StringUtils.isEmpty(endLogTime)) {
            try {
                endDate = (DateUtils.endOfDay(fmt.parse(endLogTime)));
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        if (startDate != null) {
            wrapper.gt("create_time", startDate);
        }
        if (endDate != null) {
            wrapper.lt("create_time", endDate);
        }


        wrapper.orderBy("create_time", false);
        if(StringUtils.isNotBlank(export)){
            ExcelWriter writer = null;
            OutputStream outputStream = null;
            try {
                outputStream = response.getOutputStream();

            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setCharacterEncoding("utf-8");
            String fileName="日志list"+ DateUtils.format(new Date());
            response.setHeader("Content-disposition", "attachment;filename="+ URLEncoder.encode(fileName,"utf-8")+".xlsx");
            //实例化 ExcelWriter
            TableStyle tableStyle=new TableStyle();
            tableStyle.setTableHeadBackGroundColor(IndexedColors.YELLOW);
            tableStyle.setTableContentBackGroundColor(IndexedColors.WHITE);
            writer = new ExcelWriter(outputStream, ExcelTypeEnum.XLSX, true);
            Sheet sheet = new Sheet(1, 0, LogDoExport.class);
            sheet.setSheetName("日志信息");
            sheet.setTableStyle(tableStyle);
            if(export.equals("0")){
                Page<LogDO> page = logDoService.selectPage(getPage(LogDO.class), wrapper);
                if(page.getRecords()!=null) {
                    List<LogDoExport> logDoExportList=new ArrayList<>();
                    for (LogDO logDO : page.getRecords()) {
                        String json= JSONObject.toJSONString(logDO);
                        LogDoExport logDoExport=JSON.parseObject(json,LogDoExport.class);
                        logDoExport.setStatus(logDoExport.getStatus().equals("0")?"错误":
                                logDoExport.getStatus().equals("1")?"正常":"登录");
                        logDoExportList.add(logDoExport);

                    }
                    //输出
                    if(logDoExportList.size()>0) {
                        writer.write(logDoExportList, sheet);
                        writer.finish();
                        outputStream.flush();
                    }

                }

            }else{
                wrapper=new EntityWrapper<>();
                wrapper.orderBy("create_time",false);
                Page<LogDO> page = logDoService.selectPage(new Page<LogDO>(1, 10000), wrapper);
                List<LogDoExport> logDoExportList=new ArrayList<>();
                for (LogDO logDO : page.getRecords()) {
                    String json= JSONObject.toJSONString(logDO);
                    LogDoExport logDoExport=JSON.parseObject(json,LogDoExport.class);
                    logDoExport.setStatus(logDoExport.getStatus().equals("0")?"错误":
                            logDoExport.getStatus().equals("1")?"正常":"登录");
                    logDoExportList.add(logDoExport);

                }
                //输出
                if(logDoExportList.size()>0) {
                    writer.write(logDoExportList, sheet);
                    writer.finish();
                    outputStream.flush();
                }
              }
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    response.getOutputStream().close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return  null;
        }else {
            Page<LogDO> page = logDoService.selectPage(getPage(LogDO.class), wrapper);
            Result<Page<LogDO>> data = Result.ok(page);
            return data;
        }
    }
    @Autowired
    private AppDoService appDoService;

    @ApiOperation(value = "应用日志list", notes = "应用日志list")
    @ResponseBody
    @GetMapping("/appLog/list/{appId}")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "操作名称", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "userName", value = "用户名称", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "startLogTime", value = "开始时间", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "endLogTime", value = "结束时间", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "status", value = "状态 0错误，1正常，2登录", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "export",required =false, value = "是否导出操作0导出结果条件，1导出1万条", dataType = "String", paramType = "query"),
            @ApiImplicitParam(name = "currentPage", value = "开始页数", defaultValue = "1", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "pageSize", value = "每页多少条", defaultValue = "15", dataType = "int", paramType = "query")})
    public Result<Page<LogDO>> select(
            HttpServletRequest request, HttpServletResponse response,
            @PathVariable("appId")String appId,
            @DateTime(message = "您输入的格式错误，正确的格式为：{format}") @PathParam("startLogTime") String startLogTime,
            @DateTime(message = "您输入的格式错误，正确的格式为：{format}") @PathParam("endLogTime") String endLogTime,
            @RequestParam(value = "name", required = false) String name,
            @RequestParam(value = "userName",required = false)String userName,
            String export,
            @RequestParam(value = "status", required = false) String status
    ) {
        Subject subject = SecurityUtils.getSubject();
        if(StringUtils.isBlank(export)){
            if(!subject.isPermitted("sys:log:list")) {
                throw new RException("已超出你的权限范围");
            }
        }else {
            if (!subject.isPermitted("sys:log:export")) {
                throw new RException("已超出你的权限范围");
            }

        }
       AppDo appDo= appDoService.selectById(appId);
       if(appDo!=null&&StringUtils.isNotBlank(appDo.getBackUrl())){
           try {
               URI uri=new URI(appDo.getBackUrl());
               uri=getIP(uri);
               String url=uri.getScheme()+"://"+uri.getHost()+":"+uri.getPort();
               Map<String,String> map=new HashMap<>(16);
               if(StringUtils.isNotBlank(export)) {
                   map.put("export", export);
               }
               if(StringUtils.isNotBlank(name)) {
                   map.put("name", name);
               }
               if(StringUtils.isNotBlank(userName)) {
                   map.put("userName", userName);
               }
               if(StringUtils.isNotBlank(startLogTime)) {
                   map.put("startLogTime", startLogTime);
               }
               if(StringUtils.isNotBlank(endLogTime)) {
                   map.put("endLogTime", endLogTime);
               }
               if(StringUtils.isNotBlank(status)) {
                   map.put("status", status);
               }
               int pageNumber = getParaToInt("currentPage", 1);
               int pageSize = getParaToInt("pageSize", 10);
               map.put("currentPage",String.valueOf(pageNumber));
               map.put("pageSize",String.valueOf(pageSize));
               String token=(String)SecurityUtils.getSubject().getPrincipal();
               String userAgent= JWTUtil.getUserAgent(token);
               HttpRequest httpRequest= HttpRequest.get(url+"/api/log/list",map,false);
               httpRequest.header("User-Agent",userAgent);
               httpRequest.header("Authorization",token);
               if(StringUtils.isBlank(export)){

                  String data= httpRequest.body();
                   Result<Page<LogDO>> pageResult= JSONObject.parseObject(data,Result.class);
                   return  pageResult;

               }else{
                   OutputStream outputStream = null;
                   try {
                       outputStream = response.getOutputStream();
                       response.setCharacterEncoding("utf-8");
                       response.setContentType("application/vnd.ms-excel;charset=utf-8");
                       response.setCharacterEncoding("utf-8");
                       String fileName=appDo.getName()+"-"+"日志list"+ DateUtils.format(new Date());
                       response.setHeader("Content-disposition", "attachment;filename="+ URLEncoder.encode(fileName,"utf-8")+".xlsx");
                      httpRequest.getConnection().setDoOutput(true);
                       InputStream br = httpRequest.getConnection().getInputStream();
                       byte[] buf = new byte[1024];
                       int len = 0;
                       while ((len = br.read(buf)) > 0)
                           outputStream.write(buf, 0, len);
                       br.close();
                       outputStream.flush();
                   }catch (Exception ex){
                       ex.printStackTrace();
                   }finally {
                       try {
                           response.getOutputStream().close();
                       } catch (IOException e) {
                           e.printStackTrace();
                       }
                   }
                   return  null;
               }

           } catch (URISyntaxException e) {
               e.printStackTrace();
               throw new RException("该应用配置不正确");
           }


       }else{
            throw new RException("该应用配置不正确");
        }


    }
    @ApiOperation(value = "配置后台url应用", notes = "应用list")
    @RequestMapping(value = "/appList", method = RequestMethod.GET)
    @ApiImplicitParams({
            @ApiImplicitParam(name = "currentPage", value = "开始页数", defaultValue = "1", dataType = "int", paramType = "query"),
            @ApiImplicitParam(name = "pageSize", value = "每页多少条", defaultValue = "15", dataType = "int", paramType = "query")})
    public Result<Page<AppDo>> appList(){

        Wrapper<AppDo> wrapper=new EntityWrapper<>();
        wrapper.isNotNull("back_url");

        wrapper.orderBy("id",false);
        Page<AppDo> appDoPage=appDoService.selectPage(getPage(AppDo.class),wrapper);
        return Result.ok(appDoPage);
    }

    private static URI getIP(URI uri) {
        URI effectiveURI = null;

        try {
            // URI(String scheme, String userInfo, String host, int port, String
            // path, String query,String fragment)
            effectiveURI = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), null, null, null);
        } catch (Throwable var4) {
            effectiveURI = null;
        }

        return effectiveURI;
    }

    @ApiOperation("删除日志")
    @DeleteMapping("/{id}")
    @RequiresPermissions("sys:log:delete")
    public R delete(@PathVariable("id") String id) {


        logDoService.deleteById(id);

        return R.ok();
    }

    @ApiOperation(value = "批量删除记录")
    @RequestMapping(value = "/deleteSelect", method = RequestMethod.POST)
    @RequiresPermissions("sys:log:delete")
    public R deleteSelect(@ApiParam(value = "ids集合", name = "ids", required = true) @RequestParam String[] ids) {
        try {
            if (ids != null && ids.length > 0) {
                logDoService.deleteBatchIds(Arrays.asList(ids));
            }
        } catch (Exception ex) {
            return R.error("删除失败");
        }
        return R.ok();
    }

    @Autowired
    private BackUpDoService backUpDoService;

    @ApiOperation(value = "备份日志")
    @RequestMapping(value = "/backupLog", method = RequestMethod.POST)
    @RequiresPermissions("sys:log:backup")
    public R backupLog(HttpServletRequest request) {
        boolean isSuccess = false;
        BackUpDo backUpDo = new BackUpDo();
        String backUrl = "";
        if (Cache.get("backUpLogData") != null) {
            return R.error(200, "有备份任务进行中请稍后");
        }
        Cache.add("backUpLogData", 1, 1800000);
        backUrl = logDoService.backUpLog(false);
        Cache.remove("backUpData");
        backUpDo.setBackUrl(backUrl);
        backUpDo.setSizeNum(0);
        backUpDo.setFileType(1);
        Cache.remove("backUpLogData");
        if (backUrl == null) {
            return R.error(200, "备份日志文件失败!");
        }
        if (StringUtils.isNotBlank(backUrl)) {
            backUpDoService.setBackUpData(backUpDo,backUrl);
        }
        backUpDo.setCreateTime(new Date());
        if (null == backUpDo) {
            Cache.remove("backUpLogData");
            return R.error(200, "备份日志文件失败!");
        }
        Cache.remove("backUpLogData");
        isSuccess = backUpDoService.insert(backUpDo);
        if (isSuccess) {
            return R.ok();
        } else {
            return R.error(200, "备份日志文件失败!");
        }
    }

    @ApiOperation(value = "清理日志")
    @RequestMapping(value = "/clearLog", method = RequestMethod.POST)
    @RequiresPermissions("sys:log:clear")
    @ApiImplicitParam(name = "month",dataType = "Integer", paramType = "query",value = "-1全部除了今天，1,3,6，12,一个月，3个月半年，一年", defaultValue = "-1")
    public R clearLog(Integer month) {
        //清理日志
        Integer days=1;
        if(month>0){
            days=month*30;
        }
        //删除日志文件
        FileHelperUtils.deleteFiles(FileHelperUtils.getLogSavePath(),days);
        //删除日志数据库记录
        Wrapper<LogDO> logDOWrapper=new EntityWrapper<>();
        Date pointDate = new Date();
        if(month!=-1) {
            pointDate = DateUtils.addDateDays(pointDate, 0 - days);
            pointDate = DateUtils.endOfDay(pointDate);
            logDOWrapper.le("create_time", pointDate);
        }
        logDoService.delete(logDOWrapper);
        return R.ok();
    }

    @ApiOperation(value = "下载日志")
    @GetMapping("/downloadLog")
    @RequiresPermissions("sys:log:down")
    public void downBackUp(HttpServletResponse response,
                           HttpServletRequest request) throws IOException {
        Wrapper<BackUpDo> backUpDoWrapper=new EntityWrapper<>();
        backUpDoWrapper.orderBy("create_time",false).eq("file_type",1);
        BackUpDo backUpDo= backUpDoService.selectOne(backUpDoWrapper);
        backUpDoService.downloadFile(response,backUpDo);

    }

    @ApiOperation(value = "按月份统计日志数量")
    @GetMapping("/logMonthByOperation")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "year",dataType = "String", paramType = "query",value = "年份", defaultValue = "2021"),
            @ApiImplicitParam(name = "operation",dataType = "String", paramType = "query",value = "操作类型", defaultValue = "登录")
    })
    public R logMonthStatistics(String year, String operation) {
        Wrapper<LogDO> logDOWrapper=new EntityWrapper<LogDO>();
        if(StringUtils.isBlank(year)){
            year = "2021";
        }
        if(StringUtils.isBlank(operation)){
            operation = "登录";
        }
        logDOWrapper.eq("to_char(create_time,'yyyy')", year);
        logDOWrapper.eq("operation", operation);
        logDOWrapper.groupBy("to_char(create_time,'MM')");
        logDOWrapper.orderBy("to_char(create_time,'MM')");
        logDOWrapper.setSqlSelect("count(1) num,to_char(create_time,'MM') months");

        List<Map<String, Object>> list = logDoService.selectMaps(logDOWrapper);
        if(12 < list.size()){
            // 12个月，直接返回
            return R.ok(list);
        }
        try{
            for(int i = 1; i<= 12 ; i++){
                String month = "";
                if(i < 10){
                    month = "0" + i;
                }else{
                    month = "" + i;
                }
                // 查找是否存在
                String finalMonth = month;
                List<Map<String, Object>> months = list.stream().filter(f -> f.get("months").equals(finalMonth)).collect(Collectors.toList());
                if(null == months || 0 == months.size()){
                    // 不存在该月份，追加，默认数量为0
                    Map<String, Object> resMap = new HashMap<>(16);
                    resMap.put("months", finalMonth);
                    resMap.put("num", 0);
                    list.add(resMap);
                }
            }
            // 按月份排序
            Collections.sort(list, new Comparator<Map<String, Object>>() {
                public int compare(Map<String, Object> o1, Map<String, Object> o2) {
                    Integer name1 = Integer.valueOf(o1.get("months").toString()) ;
                    Integer name2 = Integer.valueOf(o2.get("months").toString()) ;
                    return name1.compareTo(name2);
                }
            });
        }catch (Exception ex){
            ex.printStackTrace();
        }

        return R.ok(list);
    }
}